from dataclasses import dataclass
from collections import deque

@dataclass
class ArbTickEntry:
    timestamp: int
    uniswap_ask0: float
    uniswap_bid0: float
    binance_ask0: float
    binance_bid0: float

class ArbSignalCalculator(object):
  def __init__(self, queue_size = 2, enter_bar = 2.0, leave_bar = -0.5):
    self._kqueue_size = queue_size
    self._kenter_bar = enter_bar
    self._kleave_bar = leave_bar
    self.signal_queue = deque()
  
  def insert_record(self, timestamp, uniswap_ask0, uniswap_bid0, binance_ask0, binance_bid0):
    self.signal_queue.append(ArbTickEntry(timestamp, uniswap_ask0, uniswap_bid0, binance_ask0, binance_bid0))
    if (len(self.signal_queue) > self._kqueue_size):
      self.signal_queue.popleft()
  
  def is_ready(self):
    return len(self.signal_queue) >= self._kqueue_size

  def record_can_enter_buy(self, entry):
    return entry.binance_bid0 > entry.uniswap_ask0 + self._kenter_bar
  
  def record_can_enter_sell(self, entry):
    return entry.binance_ask0 < entry.uniswap_bid0 - self._kenter_bar
  
  def record_can_return_buy(self, entry):
    return entry.binance_bid0 < entry.uniswap_ask0 + self._kleave_bar
  
  def record_can_return_sell(self, entry):
    return entry.binance_ask0 > entry.uniswap_bid0 - self._kleave_bar

  def can_enter_buy(self):
    for entry in self.signal_queue:
      if not self.record_can_enter_buy(entry):
        return False
    return True
  
  def can_enter_sell(self):
    for entry in self.signal_queue:
      if not self.record_can_enter_sell(entry):
        return False
    return True
  
  def can_return_buy(self):
    for entry in self.signal_queue:
      if not self.record_can_return_buy(entry):
        return False
    return True
  
  def can_return_sell(self):
    for entry in self.signal_queue:
      if not self.record_can_return_sell(entry):
        return False
    return True
